package cn.lingyangwl.agile.misc.short_link.controller;

import cn.lingyangwl.agile.misc.short_link.manager.*;
import cn.lingyangwl.agile.misc.short_link.model.entity.*;
import cn.lingyangwl.agile.misc.short_link.model.mq.*;
import cn.lingyangwl.agile.misc.short_link.model.rqrs.*;
import cn.lingyangwl.agile.misc.short_link.service.*;
import cn.lingyangwl.agile.misc.short_link.utils.*;
import cn.lingyangwl.framework.core.http.*;
import cn.lingyangwl.framework.core.utils.*;
import cn.lingyangwl.framework.core.utils.servlet.*;
import cn.lingyangwl.framework.log.utils.*;
import cn.lingyangwl.framework.tool.core.exception.*;
import eu.bitwalker.useragentutils.UserAgent;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Objects;

/**
 * @author shenguangyang
 */
@Slf4j
@Api(tags = "访问短链")
@Controller
public class LinkApiController {
    @Resource
    private ShortLinkService shortLinkService;

    @Resource
    private ShortLinkMqManager mqManager;

    /**
     * 解析301还是302，这边是返回http code是302
     * <p>
     * 1. 为什么要用302跳转而不是301呐？
     * <p>
     * 301
     * 是永久重定向，302是临时重定向。
     * <p>
     * 短地址一经生成就不会变化，所以用301是同时对服务器压力也会有一定减少
     * <p>
     * 但是如果使用了301，无法统计到短地址被点击的次数。
     * <p>
     * 所以选择302虽然会增加服务器压力，但是有很多数据可以获取进行分析
     */
    @ApiOperation(value = "访问短链")
    @GetMapping("/s/{shortLinkCode}")
    public void dispatch(@PathVariable("shortLinkCode") String shortLinkCode,
                         HttpServletResponse response, HttpServletRequest request) {
        try {
            // 判断短链吗是否合规
            if (isLetterDigit(shortLinkCode)) {
                ShortLink shortLink = shortLinkService.getLinkByCode(shortLinkCode);
                // 判断是否过期可用
                if (isVisitable(shortLink)) {
                    buildAndSendAccessLog(shortLink, request);
                    response.setHeader("Location", ShortLinkUtils.removeUrlPrefix(shortLink.getOriginalUrl()));
                    response.setStatus(HttpStatus.FOUND.value());
                } else {
                    response.setStatus(HttpStatus.NOT_FOUND.value());
                }
            }
        } catch (Exception e) {
            log.error("error: {}", e.getMessage());
            String msg = e instanceof BizException ? e.getMessage() : "处理异常" + "-" + MDCTraceUtils.getTraceId();
            try {
                ServletUtils.writeHtml(msg);
            } catch (Exception ex) {
                throw new BizException(msg);
            }
        }
    }

    private void buildAndSendAccessLog(ShortLink shortLink, HttpServletRequest request) {
        try {
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
            AccessLog accessLog = new AccessLogResp();
            accessLog.setBrowser(userAgent.getBrowser().getName())
                    .setGroupId(shortLink.getGroupId()).setCode(shortLink.getCode()).setLinkId(shortLink.getId())
                    .setDomainId(shortLink.getDomainId()).setTenantId(shortLink.getTenantId())
                    .setOs(userAgent.getOperatingSystem().getName()).setIp(IpUtils.getRequestIp(new HttpRequestServletManager(request)));
            mqManager.send(SlAccessLogSaveMQ.build(accessLog));
        } catch (Exception e) {
            log.error("构建并发送访问日志异常, error: {}", e.getMessage());
        }
    }

    private boolean isVisitable(ShortLink linkGroupManager) {
        if (Objects.isNull(linkGroupManager)) {
            return false;
        }
        if (ShortLink.Status.EXPIRED.equals(linkGroupManager.getStatus())) {
            return false;
        }

        return Objects.isNull(linkGroupManager.getExpireTime()) || !new Date().after(linkGroupManager.getExpireTime());
    }

    /**
     * 仅包含数字和字母
     */
    public static boolean isLetterDigit(String str) {
        String regex = "^[a-z0-9A-Z]+$";
        return str.matches(regex);
    }
}
